#ifndef __VIDEODECODE_H__
#define __VIDEODECODE_H__

#include "common/mlog.h"
#include "common/utils.h"
#include "common/errors.h"
#include "rtmp/rtmp_reader.h"
#include <pthread.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif
    
#include "libavcodec/avcodec.h"
#include "libavutil/mem.h"
#include "libavutil/pixfmt.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
    


typedef void (*frame_available_cb)();
typedef void (*error_cb)(int error);
typedef void (*video_ready_cb)(int width, int height);
typedef void (*statis_info_cb)(int bitrate, int max_bitrate, int recv_pkts, int decoded_pkts, int discard_pkts);

typedef bool (*create_decoder_cb)(uint8_t* extradata, int extradata_size, int video_width, int video_height);
typedef void (*destroy_decoder_cb)();
/**
 * 外部解码器解码回调回函，解码器可以直接显示视频帧，或返回解码后的视频帧数据，由CVideoDecoder处理显示
 * 参数：
 *    data        要解码的视频帧数据，如果data=NULL,则表示本次调用是要刷新解码后的帧数据，由解码器检查是否有解码后的帧数据，有则显示或返回帧内存
 *  size        视频帧大小
 *    decoded_frame    由外部解码器分配内存并填充解码后的帧数据（yuv420p格式），内存分配要用malloc()
 * 返回值：
 *        < 0        错误，将会导致结束解码流程
 *        = 0        外部解码器未准备好本次解码，需要等待再重新调用解码
 *      = 1        解码成功，并且如果framedata返回不为NULL，则表示有解码后的视频帧(yuv420p格式）
 * 说明：
 *        如果外部解码器解码后直接显示，则framedata必须返回NULL。
 */
typedef int (*decode_frame_cb)(uint8_t* data, int size, char** decoded_frame);

class CVideoDecoder
{
public:
    CVideoDecoder(frame_available_cb on_frame_cb, error_cb on_error_cb, video_ready_cb on_ready_cb, statis_info_cb on_statis_cb);
    ~CVideoDecoder();
    
public:
    void start(const char* server, const char* id, int channel);
    void stop();
    void swapChannel(int channel);
    void saveStream(const char* serial_no);
    void registerDecoder(create_decoder_cb create_cb, destroy_decoder_cb destroy_cb, decode_frame_cb decode_cb);
    char* getDecodedFrame();
    void freeDecodedFrame(char* frameptr);
    
private:
#define MAX_PACKETS        30
#define MAX_FRAMES        3
    
    enum STATE {
        STAT_IDLE = 0,
        STAT_START, STAT_STOP, STAT_READY,
        STAT_ERROR,
    };
    
    struct video_packet {
        char* data;
        int size;
        bool key;
    };
    
    // 统计信息
    struct statis_info {
        int64_t last_time;
        int rcvbytes;
        int rcvpkts;
        int max_bitrate;
        int dec_pkts;
        int discard_pkts;
    };
    
public:
    CRtmpReader* mRtmpReader;
    
    int mVideoWidth;
    int mVideoHeight;
    
private:
    int mState;
    
    char mServer[64];
    char mID[64];
    int mChannel;
    
    AVCodecContext* mDecodeCtx;
#ifdef ANDROID
    struct SwsContext* mSwsCtx;
#endif
    bool mUseMediaCodec;
    bool mUseExtraDecoder;
    bool mFirstInputFrame;
    int64_t mPts;
    bool mNeedKeyFrame;
    char mExtradata[128];
    int mExtradataSize;
    
    bool mCanNotifyRenderer;
    
    // RTMP视频包
    struct video_packet mPackets[MAX_PACKETS];
    int mPacketWindex;
    int mPacketRindex;
    int mPacketCount;
    
    // 解码后的视频帧
    char* mFrames[MAX_PACKETS];
    int mFrameWindex;
    int mFrameRindex;
    int mFrameCount;
    
    // 统计信息
    struct statis_info mStatisInfo;
    
    create_decoder_cb mOnDecoderCreateHandler;
    destroy_decoder_cb mOnDecoderDestroyHandler;
    decode_frame_cb mOnDecoderDecodeHandler;
    
    frame_available_cb mOnFrameAvailableHandler;
    error_cb mOnErrorHandler;
    video_ready_cb mOnVideoReadyHandler;
    statis_info_cb mOnStatisInfoHandler;
    
    pthread_t mThread;
    pthread_mutex_t mLock;
    bool mIsRunning;
    
private:
    static void* thread_func(void* p) { ((CVideoDecoder*)p)->onThread(); return NULL; }
    void onThread();
    
    static void packet_handler(char *data, int size, void* param) { ((CVideoDecoder*)param)->onVideoPacket(data, size); }
    void onVideoPacket(char* data, int size);
    
    bool createDecoder(uint8_t* extradata, int extradata_size, char* payload, int payload_size);
    void destroyDecoder();
    bool decodeFrame();
    void putDecodedFrame(char* frameptr);
    void putDecodedAVFrame(AVFrame* frame);
    void freeAllFrames();
    void freeAllPackets();
};


    
#ifdef __cplusplus
}
#endif

#endif

